﻿// AmiSessionManager.cs
// Copyright © 2013 Alex Forster

namespace Ami.SessionManager
{
	using System;
	using System.Diagnostics.Contracts;
	using System.Security.Cryptography;
	using System.Text;
	using System.Threading.Tasks;
	using Ami.ByteArrayExtensions;

	public sealed class AmiSessionManager
	{
		private AmiClient Client;

		private enum SessionStates
		{
			Disconnected,
			Connected,
		}

		private SessionStates SessionState;

		public AmiSessionManager( AmiClient client )
		{
			Contract.Requires<ArgumentNullException>( client != null );

			this.Client = client;

			this.SessionState = SessionStates.Disconnected;
		}

		public async Task<Boolean> Login( String username, String password )
		{
			Contract.Requires<ArgumentNullException>( username != null );
			Contract.Requires<ArgumentNullException>( password != null );

			Contract.Assert( this.SessionState == SessionStates.Disconnected );

			var challengeMessage = new AmiActionMessage( "Challenge" )
			{
				{ "AuthType", "MD5" },
			};

			AmiResponseMessage response;

			response = await this.Client.Publish( challengeMessage );

			if( response == null || response.Response.Equals( "Success" ) == false )
			{
				return false;
			}

			var challenge = response["Challenge"];

			var md5er = MD5.Create();

			var rawBytes = Encoding.ASCII.GetBytes( challenge ).Append( Encoding.ASCII.GetBytes( password ) );

			var hashedBytes = md5er.ComputeHash( rawBytes );

			var key = "";

			for( var i = 0; i < hashedBytes.Length; i++ )
			{
				key += hashedBytes[i].ToString( "x2" );
			}

			var loginMessage = new AmiActionMessage( "Login" )
			{
				{ "AuthType", "MD5" },
				{ "Username", username },
				{ "Key", key },
			};

			response = await this.Client.Publish( loginMessage );

			if( response == null || response.Response.Equals( "Success" ) == false )
			{
				return false;
			}

			this.SessionState = SessionStates.Connected;
			return true;
		}

		public async Task<Boolean> Logoff()
		{
			Contract.Assert( this.SessionState == SessionStates.Connected );

			var logoffMessage = new AmiActionMessage( "Logoff" );

			var response = await this.Client.Publish( logoffMessage );

			if( response == null || response.Response.Equals( "Goodbye" ) == false )
			{
				return false;
			}

			this.SessionState = SessionStates.Disconnected;
			return true;
		}

		public async Task<Boolean> Ping()
		{
			Contract.Assert( this.SessionState == SessionStates.Connected );

			var pingMessage = new AmiActionMessage( "Ping" );

			var response = await this.Client.Publish( pingMessage );

			return response != null && response.Response.Equals( "Pong" );
		}

		public async Task<Boolean> DisableAllEvents()
		{
			Contract.Assert( this.SessionState == SessionStates.Connected );

			var disableMessage = new AmiActionMessage( "Events" )
			{
				{ "EventMask", "off" },
			};

			var response = await this.Client.Publish( disableMessage );

			return response != null && response.Response.Equals( "Success" );
		}

		public async Task<Boolean> EnableEvents( params String[] events )
		{
			Contract.Assert( this.SessionState == SessionStates.Connected );

			var enableMessage = new AmiActionMessage( "Events" )
			{
				{ "EventMask", String.Join( ",", events ) },
			};

			var response = await this.Client.Publish( enableMessage );

			return response != null && response.Response.Equals( "Success" );
		}
	}
}